package api

import (
	"errors"
	"fmt"
	"server/global"
	"server/model/databases"
	"server/model/request"
	"server/model/response"
	"server/utils"
	"time"

	"github.com/gin-contrib/sessions"
	"github.com/gin-gonic/gin"
	"github.com/redis/go-redis/v9"
	"go.uber.org/zap"
)

type UserApi struct {
}

// Register 用户注册
// @Tags      用户接口
// @Summary   用户注册
// @Description 新用户注册
// @accept    application/json
// @Produce   application/json
// @Param     data  body      request.Register  true  "用户注册"
// @Success   200   {object}  response.Response{msg=string}  "注册成功"
// @Router    /user/register [post]
func (u *UserApi) Register(c *gin.Context) {
	var req request.Register
	err := c.ShouldBindJSON(&req)
	if err != nil {
		response.FailWithMessage(err.Error(), c)
		return
	}
	session := sessions.Default(c)
	// 判断传入的邮箱和发验证码的邮箱是否一致
	saveEmail := session.Get("email")
	if saveEmail == nil || saveEmail.(string) != req.Email {
		global.Log.Warn("This email doesn't match the email to be verified")
		response.FailWithMessage("This email doesn't match the email to be verified", c)
		return
	}
	// 验证码是否正确
	saveVerificationCode := session.Get("verification_code")
	if saveVerificationCode == nil || saveVerificationCode.(string) != req.VerificationCode {
		response.FailWithMessage("This email verification code Invalid ", c)
		return
	}
	// 验证码是否过期
	saveExpireTime := session.Get("expire_time")
	if saveExpireTime.(int64) < time.Now().Unix() {
		response.FailWithMessage("The verification code has expired, please resend it", c)
		return
	}

	user := databases.User{Username: req.Username, Email: req.Email, Password: req.Password}
	fmt.Printf("-----user:%#v\n", user)
	uu, err := userService.Register(user)
	if err != nil {
		global.Log.Error("failed to register user", zap.Error(err))
		response.FailWithMessage("failed to register user ", c)
		return
	}
	u.TokenNext(c, uu)
}

// Login 用户登录
// @Tags      用户接口
// @Summary   用户登录
// @Description 用户登录
// @accept    application/json
// @Produce   application/json
// @Param     data  body      request.Login  true  "用户登录"
// @Success   200   {object}  response.Login{msg=string}  "登录成功"
// @Router    /user/login [post]
func (u *UserApi) Login(c *gin.Context) {
	switch c.Query("flag") {
	case "email":
		u.EmailLogin(c)
	case "qq":
		u.QQLogin(c)
	default:
		u.EmailLogin(c)
	}
}

// EmailLogin 邮箱登录
func (u *UserApi) EmailLogin(c *gin.Context) {
	var req request.Login
	err := c.ShouldBindJSON(&req)
	if err != nil {
		response.FailWithMessage(err.Error(), c)
		return
	}

	if store.Verify(req.CaptchaID, req.Captcha, true) {
		user := databases.User{Email: req.Email, Password: req.Password}
		uu, err := userService.EmailLogin(user)
		if err != nil {
			global.Log.Error("Failed to login:", zap.Error(err))
			response.FailWithMessage("Failed to login", c)
			return
		}
		u.TokenNext(c, uu)
		return
	}
	response.FailWithMessage("Incorrect verification code", c)
}

// QQLogin QQ登录
func (u *UserApi) QQLogin(c *gin.Context) {
	code := c.Query("code")
	if code == "" {
		response.FailWithMessage("Code is required", c)
		return
	}
	// 获取访问令牌
	accessTokenResponse, err := qqService.GetAccessTokenByCode(code)
	if err != nil || accessTokenResponse.Openid == "" {
		global.Log.Error("Invalid code", zap.Error(err))
		response.FailWithMessage("Invalid code", c)
		return
	}

	// 根据访问令牌进行QQ登录
	user, err := userService.QQLogin(accessTokenResponse)
	if err != nil {
		global.Log.Error("Failed to login:", zap.Error(err))
		response.FailWithMessage("Failed to login", c)
		return
	}

	// 登录成功后生成 token
	u.TokenNext(c, user)
}

// TokenNext 签发jwt token
func (u *UserApi) TokenNext(c *gin.Context, user databases.User) {
	// 用户是否被冻结
	if user.Freeze {
		global.Log.Warn("The user is frozen, contact the administrator", zap.String("username", user.Username))
		response.FailWithMessage("The user is frozen, contact the administrator", c)
		return
	}

	baseClaim := request.BaseClaims{
		UserID: user.ID,
		UUID:   user.UUID,
		RoleID: user.RoleID,
	}

	j := utils.NewJwt()
	// 创建accessToken
	accessClaim := j.CreateAccessClaims(baseClaim)
	accessToken, err := j.CreateAccessToken(accessClaim)
	if err != nil {
		global.Log.Error("Failed to get accessToken:", zap.Error(err))
		response.FailWithMessage("Failed to get accessToken", c)
		return
	}
	// 创建refreshToken
	refreshClaim := j.CreateRefreshClaims(baseClaim)
	refreshToken, err := j.CreateRefreshToken(refreshClaim)
	if err != nil {
		global.Log.Error("Failed to get refreshToken:", zap.Error(err))
		response.FailWithMessage("Failed to get refreshToken", c)
		return
	}

	// 未开启异地登录拦截
	if !global.Config.System.UseMultipoint {
		// 设置Refresh Token
		utils.SetRefreshToken(c, refreshToken, int(refreshClaim.ExpiresAt.Unix()-time.Now().Unix()))
		c.Set("user_id", user.ID)
		response.OkWithDetailed(response.Login{
			User:                 user,
			AccessToken:          accessToken,
			AccessTokenExpiresAt: accessClaim.ExpiresAt.Unix() * 1000, // 毫秒级
		}, "successfully login", c)
	}

	// 开启异地登录拦截时逻辑
	if jwtStr, err := jwtService.GetRedisJWT(c, user.UUID); errors.Is(err, redis.Nil) {
		// 不存在则从新存储到redis
		if err := jwtService.SetRedisJWT(c, refreshToken, user.UUID); err != nil {
			global.Log.Error("Failed to set login status:", zap.Error(err))
			response.FailWithMessage("Failed to set login status", c)
			return
		}
		// 设置refresh token 到cookie中
		utils.SetRefreshToken(c, refreshToken, int(refreshClaim.ExpiresAt.Unix()-time.Now().Unix()))
		c.Set("user_id", user.ID)
		response.OkWithDetailed(response.Login{
			User:                 user,
			AccessToken:          accessToken,
			AccessTokenExpiresAt: accessClaim.ExpiresAt.Unix() * 1000,
		}, "successfully login", c)
	} else if err != nil {
		global.Log.Error("Failed to set login status:", zap.Error(err))
		response.FailWithMessage("Failed to set login status", c)
	} else {
		// Redis 中已存在该用户的 JWT，将旧的 JWT 加入黑名单，并设置新的 token
		var blackList databases.JwtBlacklist
		blackList.Jwt = jwtStr
		if err := jwtService.JoinInBlacklist(blackList); err != nil {
			global.Log.Error("Failed to invalidate jwt:", zap.Error(err))
			response.FailWithMessage("Failed to invalidate jwt", c)
			return
		}
		// 新token 存储到redis中
		if err := jwtService.SetRedisJWT(c, refreshToken, user.UUID); err != nil {
			global.Log.Error("Failed to set login status:", zap.Error(err))
			response.FailWithMessage("Failed to set login status", c)
			return
		}
		// 设置refresh token 到cookie中
		utils.SetRefreshToken(c, refreshToken, int(refreshClaim.ExpiresAt.Unix()-time.Now().Unix()))
		c.Set("user_id", user.ID)
		response.OkWithDetailed(response.Login{
			User:                 user,
			AccessToken:          accessToken,
			AccessTokenExpiresAt: accessClaim.ExpiresAt.Unix() * 1000,
		}, "successfully login", c)
	}
}

// ForgotPassword 重置用户密码
// @Tags      用户接口
// @Summary   重置用户密码
// @Description 重置用户密码
// @accept    application/json
// @Produce   application/json
// @Param     data  body      request.ForgotPassword  true  "密码重置成功"
// @Success   200   {object}  response.Response{msg=string}  "密码重置成功"
// @Router    /user/forgotPassword [post]
func (u *UserApi) ForgotPassword(c *gin.Context) {
	var req request.ForgotPassword
	err := c.ShouldBindJSON(&req)
	if err != nil {
		response.FailWithMessage(err.Error(), c)
		return
	}

	session := sessions.Default(c)
	// 判断传入的邮箱和发验证码的邮箱是否一致
	saveEmail := session.Get("email")
	if saveEmail == nil || saveEmail.(string) != req.Email {
		global.Log.Warn("This email doesn't match the email to be verified")
		response.FailWithMessage("This email doesn't match the email to be verified", c)
		return
	}
	// 验证码是否正确
	saveVerificationCode := session.Get("verification_code")
	if saveVerificationCode == nil || saveVerificationCode.(string) != req.VerificationCode {
		response.FailWithMessage("This email verification code Invalid ", c)
		return
	}
	// 验证码是否过期
	saveExpireTime := session.Get("expire_time")
	if saveExpireTime.(int64) < time.Now().Unix() {
		response.FailWithMessage("The verification code has expired, please resend it", c)
		return
	}
	err = userService.ForgotPassword(req)
	if err != nil {
		global.Log.Error("Failed to retrieve the password:", zap.Error(err))
		response.FailWithMessage("Failed to retrieve the password", c)
		return
	}
	response.OkWithMessage("Successfully retrieved", c)
}

// UserCard 获取用户卡信息
// @Tags      用户接口
// @Summary   获取用户卡信息
// @Description 获取用户卡信息
// @accept    application/json
// @Produce   application/json
// @Param     uuid  query     string  true  "获取用户卡信息"
// @Success   200   {object}  response.UserCard  "获取用户卡信息"
// @Router    /user/card [get]
func (u *UserApi) UserCard(c *gin.Context) {
	var req request.UserCard
	err := c.ShouldBindQuery(&req)
	if err != nil {
		response.FailWithMessage(err.Error(), c)
		return
	}
	userCard, err := userService.UserCard(req)
	if err != nil {
		global.Log.Error("Failed to get card:", zap.Error(err))
		response.FailWithMessage("Failed to get card", c)
		return
	}
	response.OkWithData(userCard, c)
}

// Logout 用户登出
// @Tags      用户接口
// @Summary   用户登出
// @Description 用户登出
// @accept    application/json
// @Produce   application/json
// @Success   200   {object}  string  "用户登出"
// @Router    /user/logout [post]
func (u *UserApi) Logout(c *gin.Context) {
	userService.Logout(c)
	response.OkWithMessage("Successfully logged out", c)
}

// UserResetPassword 用户修改密码
// @Tags      用户接口
// @Summary   用户修改密码
// @Description 用户修改密码
// @accept    application/json
// @Produce   application/json
// @Param     data  body      request.UserResetPassword  true  "用户修改密码"
// @Success   200   {object}  string  "用户修改密码成功"
// @Router    /user/resetPassword [put]
func (u *UserApi) UserResetPassword(c *gin.Context) {
	var req request.UserResetPassword
	err := c.ShouldBindJSON(&req)
	if err != nil {
		response.FailWithMessage(err.Error(), c)
		return
	}
	req.UserID = utils.GetUserID(c)
	err = userService.UserResetPassword(req)
	if err != nil {
		global.Log.Error("Failed to modify:", zap.Error(err))
		response.FailWithMessage("Failed to modify, original password does not match the current account", c)
		return
	}
	response.OkWithMessage("Successfully changed password, please log in again", c)
	u.Logout(c)
}

// UserInfo 获取用户信息
// @Tags      用户接口
// @Summary   获取用户信息
// @Description 获取用户信息
// @accept    application/json
// @Produce   application/json
// @Success   200   {object}  string  "获取用户信息成功"
// @Router    /user/info [get]
func (u *UserApi) UserInfo(c *gin.Context) {
	userId := utils.GetUserID(c)
	user, err := userService.UserInfo(userId)
	if err != nil {
		global.Log.Error("Failed to get user info:", zap.Error(err))
		response.FailWithMessage("Failed to get user info", c)
		return
	}
	response.OkWithData(user, c)
}

// UserChangeInfo 修改用户信息
// @Tags      用户接口
// @Summary   修改用户信息
// @Description 修改用户信息
// @accept    application/json
// @Produce   application/json
// @Param     data  body      request.UserChangeInfo  true  "用户修改密码"
// @Success   200   {object}  string  "修改用户信息"
// @Router    /user/changeInfo [put]
func (u *UserApi) UserChangeInfo(c *gin.Context) {
	var req request.UserChangeInfo
	err := c.ShouldBindJSON(&req)
	if err != nil {
		response.FailWithMessage(err.Error(), c)
		return
	}
	req.UserID = utils.GetUserID(c)
	err = userService.UserChangeInfo(req)
	if err != nil {
		global.Log.Error("Failed to change user info:", zap.Error(err))
		response.FailWithMessage("Failed to change user info", c)
		return
	}
	response.OkWithMessage("Successfully changed user info", c)
}

// UserWeather 获取用户天气信息
// @Tags      用户接口
// @Summary   获取用户天气信息
// @Description 获取用户天气信息
// @accept    application/json
// @Produce   application/json
// @Success   200   {object}  string  "获取用户天气信息"
// @Router    /user/weather [get]
func (u *UserApi) UserWeather(c *gin.Context) {
	ip := c.ClientIP()
	//ip := "110.184.65.183"
	weather, err := userService.UserWeather(c, ip)
	if err != nil {
		global.Log.Error("Failed to get user weather:", zap.Error(err))
		response.FailWithMessage("Failed to get user weather", c)
		return
	}
	response.OkWithData(weather, c)
}

// UserChart 获取用户图表数据，登录和注册人数
// @Tags      用户接口
// @Summary   获取用户统计数据
// @Description 获取用户统计数据
// @accept    application/json
// @Produce   application/json
// @Param     data  query      request.UserChart  true  "获取用户统计数据"
// @Success   200   {object}  response.UserChart  "获取用户统计数据成功"
// @Router    /user/chart [get]
func (u *UserApi) UserChart(c *gin.Context) {
	var req request.UserChart
	err := c.ShouldBindQuery(&req)
	if err != nil {
		response.FailWithMessage(err.Error(), c)
		return
	}
	data, err := userService.UserChart(req)
	if err != nil {
		global.Log.Error("Failed to get user chart:", zap.Error(err))
		response.FailWithMessage("Failed to get user chart", c)
		return
	}
	response.OkWithData(data, c)
}

// UserList 获取用户列表信息
// @Tags      用户接口
// @Summary   获取用户列表信息
// @Description 获取用户列表信息
// @accept    application/json
// @Produce   application/json
// @Param     pageInfo  query    request.UserList  true  "查询参数"
// @Success   200   {object}  response.PageResult  "获取用户列表信息"
// @Router    /user/list [get]
func (u *UserApi) UserList(c *gin.Context) {
	var pageInfo request.UserList
	err := c.ShouldBindQuery(&pageInfo)
	if err != nil {
		response.FailWithMessage(err.Error(), c)
		return
	}
	list, total, err := userService.UserList(pageInfo)
	if err != nil {
		global.Log.Error("Failed to get user list:", zap.Error(err))
		response.FailWithMessage("Failed to get user list", c)
		return
	}
	response.OkWithData(response.PageResult{
		List:  list,
		Total: total,
	}, c)
}

// UserLoginList 获取用户登录列表信息
// @Tags      用户接口
// @Summary   获取用户登录列表信息
// @Description 获取用户登录列表信息
// @accept    application/json
// @Produce   application/json
// @Param     pageInfo  query    request.UserLoginList  true  "查询参数"
// @Success   200   {object}  response.PageResult  "获取用户登录列表信息成功"
// @Router    /user/loginList [get]
func (u *UserApi) UserLoginList(c *gin.Context) {
	var pageInfo request.UserLoginList
	err := c.ShouldBindQuery(&pageInfo)
	if err != nil {
		response.FailWithMessage(err.Error(), c)
		return
	}
	list, total, err := userService.UserLoginList(pageInfo)
	if err != nil {
		global.Log.Error("Failed to get user login list:", zap.Error(err))
		response.FailWithMessage("Failed to get user login list", c)
		return
	}
	response.OkWithData(response.PageResult{
		List:  list,
		Total: total,
	}, c)
}

// UserFreeze 用户冻结
// @Tags      用户接口
// @Summary   用户冻结
// @Description 用户冻结
// @accept    application/json
// @Produce   application/json
// @Param     pageInfo  query    request.UserOperation  true  "用户冻结"
// @Success   200   {object}  string  "用户冻结成功"
// @Router    /user/freeze [put]
func (u *UserApi) UserFreeze(c *gin.Context) {
	var req request.UserOperation
	err := c.ShouldBindJSON(&req)
	if err != nil {
		response.FailWithMessage(err.Error(), c)
		return
	}
	err = userService.UserFreeze(c, req)
	if err != nil {
		global.Log.Error("Failed to freeze user:", zap.Error(err))
		response.FailWithMessage("Failed to freeze user", c)
		return
	}
	response.OkWithMessage("Successfully freeze user", c)
}

// UserUnFreeze 解冻用户
// @Tags      用户接口
// @Summary   解冻用户
// @Description 解冻用户
// @accept    application/json
// @Produce   application/json
// @Param     pageInfo  query    request.UserOperation  true  "解冻用户"
// @Success   200   {object}  string  "解冻用户成功"
// @Router    /user/unfreeze [put]
func (u *UserApi) UserUnFreeze(c *gin.Context) {
	var req request.UserOperation
	err := c.ShouldBindJSON(&req)
	if err != nil {
		response.FailWithMessage(err.Error(), c)
		return
	}
	err = userService.UserUnFreeze(req)
	if err != nil {
		global.Log.Error("Failed to unfreeze user:", zap.Error(err))
		response.FailWithMessage("Failed to unfreeze user", c)
		return
	}
	response.OkWithMessage("Successfully unfreeze user", c)
}
